/*******************************************************************************

                     LVGL输入法之拼音输入法实现
本输入法负责管理输入法软键盘接收，以及一行的选择显示，仅支持触模不支持实体按键
暂仅支持GB2312编码的汉字
拼音选择状态时如：<< a ai ao au >>
汉字选择状态时如: a << 啊 阿 吖 嗄 >>
<<或>>为上下与下页键，有上或下页时自动出现
*******************************************************************************/
#include "lvIME_Base.h"
#include "GB2312_Utf8.h"
#include <string.h>

//---------------------------动态映射表-------------------------------
//最多20个备选+左右符号
static const char * BTNMAP_CONST _KbBtnMap[] = {
  lvIME_SignBuf[0], lvIME_SignBuf[1], lvIME_SignBuf[2], lvIME_SignBuf[3], lvIME_SignBuf[4],
  lvIME_SignBuf[5], lvIME_SignBuf[6], lvIME_SignBuf[7], lvIME_SignBuf[8], lvIME_SignBuf[9],
  lvIME_SignBuf[10], lvIME_SignBuf[11], lvIME_SignBuf[12], lvIME_SignBuf[13], lvIME_SignBuf[14],
  lvIME_SignBuf[15], lvIME_SignBuf[16], lvIME_SignBuf[17], lvIME_SignBuf[18], lvIME_SignBuf[19],
  lvIME_SignBuf[20],""};

//----------------------更新按键MAP---------------------------------
//返回创建的对像
static void _UpdateMap(struct _lvIME_PinYin *pPinYin)
{
  //等待状态为空白
  if(pPinYin->InState == LV_IME_PINYIN_WAIT_IN){
    strcpy(lvIME_SignBuf[0], "拼音"); //切换时输入法提示，长了放不下。
    lvIME_SignBuf[1][0]  = '\0';  //结束
    //重新设置并更新
    lv_buttonmatrix_set_map(pPinYin->pObj, (const char **)_KbBtnMap);
    return;
  }
  //汉字选择状态如：a << 啊 阿 吖 嗄 >>, 填充最左拼音
  char *pSignBuf = lvIME_SignBuf[0];
  if(pPinYin->InState == LV_IME_PINYIN_CH_SEL){
    const char *pPyStr;
    if(pPinYin->IsKb26) pPyStr = pPinYin->PinYin;
    else{
      const struct _PyIndex *const *ppPyIndex;
      ppPyIndex = NumKeyPyIme_pGetCur(&pPinYin->PY) + pPinYin->CurPinYin;
      pPyStr = (*ppPyIndex)->Py;
    }
    strcpy(pSignBuf, pPyStr);
    pSignBuf += LV_IME_SIGN_BYTE;
  }

  unsigned char ContentBtnCount = pPinYin->ContentBtnCount;
  unsigned char HavePrvNextBtn = pPinYin->HavePrvNextBtn;
  //填认最左的 << 即上一页键
  if(HavePrvNextBtn & 0x01){//有左上翻页键
    pSignBuf[0]  = '<';
    pSignBuf[1]  = '<';
    pSignBuf[2]  = '\0';
    pSignBuf += LV_IME_SIGN_BYTE;
  }
  pSignBuf += pPinYin->ContentBtnCount * LV_IME_SIGN_BYTE;  //用户按钮
  if(HavePrvNextBtn & 0x02){//有右下翻页键
    pSignBuf[0]  = '>';
    pSignBuf[1]  = '>';
    pSignBuf[2]  = '\0';
    pSignBuf += LV_IME_SIGN_BYTE;
  }
  pSignBuf[0]  = '\0';//表示按钮结束
  //重新设置并更新
  lv_buttonmatrix_set_map(pPinYin->pObj, (const char **)_KbBtnMap);
}

//-----------------------------更新按钮显示-----------------------------------
//同时更新ContentBtnMax/HavePrvNextBtn/ContentBtnCount
static void _UpdateDisp(struct _lvIME_PinYin *pPinYin)
{
  //等待输入状态: 显示空白
  if(pPinYin->InState == LV_IME_PINYIN_WAIT_IN){
    _UpdateMap(pPinYin);
    return;
  }
  unsigned char ContentBtnMax = pPinYin->BtnMax; //总按钮数
  unsigned char HavePrvNextBtn = 0;  //b0有上一页按钮，b1有下一页按钮
  unsigned char ContentBtnCount;
  //拼音输入状态(9键输入时有)， 更新显示如 << a ao au >>
  if(pPinYin->InState == LV_IME_PINYIN_PINYING_IN){
    unsigned short Base = pPinYin->PinYinStart;
    if(Base){//上一页按钮占位
      HavePrvNextBtn |= 0x01;
      ContentBtnMax--;
    }
    unsigned short End = NumKeyPyIme_GetCurCount(&pPinYin->PY);
    if((Base + ContentBtnMax) <= End){ //一页装不下了
      HavePrvNextBtn |= 0x02;
      ContentBtnMax--;
      End = Base + ContentBtnMax;
      ContentBtnCount = ContentBtnMax;
    }
    else ContentBtnCount = End - Base;//一页能装下
    //填充lvIME_SignBuf[]区拼音字符
    char *pSignBuf = lvIME_SignBuf[pPinYin->HavePrvNextBtn & 0x01];
    const struct _PyIndex *const *ppPyIndex;
    ppPyIndex = NumKeyPyIme_pGetCur(&pPinYin->PY) + Base;
    for(; Base < End; Base++, pSignBuf += LV_IME_SIGN_BYTE, ppPyIndex++){
      strcpy(pSignBuf, (*ppPyIndex)->Py);
    }
  }
  //汉字选择状态， 显示如 a << 啊 阿 吖 嗄 >>
  else{
    ContentBtnMax-= 2;//最左拼音显示占两格
    unsigned short Base = pPinYin->ChStart;
    if(Base){//上一页按钮占位
      HavePrvNextBtn |= 0x01;
      ContentBtnMax--;
    }
    const char *pCh;
    if(pPinYin->IsKb26) pCh = pPinYin->pCh;
    else{
      const struct _PyIndex *const *ppPyIndex;
      ppPyIndex = NumKeyPyIme_pGetCur(&pPinYin->PY) + pPinYin->CurPinYin;
      pCh = (*ppPyIndex)->Pymb;
    }
    unsigned short End = strlen(pCh) >> 1; //GB2312编码
    if((Base + ContentBtnMax) <= End){ //一页装不下了
      HavePrvNextBtn |= 0x02;
      ContentBtnMax--;
      End = Base + ContentBtnMax;
      ContentBtnCount = ContentBtnMax;
    }
    else ContentBtnCount = End - Base;//一页能装下
    //GB2312转换为UTF8码填充
    char *pSignBuf = lvIME_SignBuf[1 + (HavePrvNextBtn & 0x01)];
    pCh += Base << 1; //全角字符
    for(; Base < End; Base++, pSignBuf += LV_IME_SIGN_BYTE,pCh += 2){
      pSignBuf[Code_GB2312ToUtf8(pCh, 2, pSignBuf, LV_IME_SIGN_BYTE - 1)] = '\0';
    }
  }
  //更新至结构
  pPinYin->ContentBtnMax = ContentBtnMax;
  pPinYin->HavePrvNextBtn = HavePrvNextBtn;
  pPinYin->ContentBtnCount = ContentBtnCount;
  _UpdateMap(pPinYin);
}
//-----------------------返回等待状态,并初始化相关变量----------------------------
static void _ToWaitIn(struct _lvIME_PinYin *pPinYin)
{
  pPinYin->InState = LV_IME_PINYIN_WAIT_IN;
  memset(&pPinYin->PY, 0, sizeof(PinYinIME_t));
  pPinYin->PinYin[0] = '\0';
  pPinYin->PinYinCount = 0;

  pPinYin->PinYinStart = 0;
  pPinYin->ChStart = 0;
  pPinYin->CurPinYin = 0;
}

//-----------------------到拼音输入状态,并初始化相关变量-------------------------
static void _ToPinYinIn(struct _lvIME_PinYin *pPinYin)
{
  pPinYin->InState = LV_IME_PINYIN_PINYING_IN;
  pPinYin->PinYinStart = 0;
  pPinYin->ChStart = 0;
  pPinYin->CurPinYin = 0;
}

//-----------------------到汉字选择状态,并初始化相关变量-------------------------
static void _ToChSel(struct _lvIME_PinYin *pPinYin)
{
  pPinYin->InState = LV_IME_PINYIN_CH_SEL;
  pPinYin->ChStart = 0;
}

//-------------------------------事件回调实现---------------------------------
static void _EventHandler(lv_event_t * e)
{
  lv_event_code_t Code = lv_event_get_code(e);
  if(Code != LV_EVENT_VALUE_CHANGED) return;
  //lv_obj_t * obj = lv_event_get_target_obj(e);
  struct _lvIME_PinYin *pPinYin = (struct _lvIME_PinYin *)lv_event_get_user_data(e);
  if(pPinYin->InState == LV_IME_PINYIN_WAIT_IN) return;//等待状态不响应
  unsigned char KeyId = lv_btnmatrix_get_selected_btn(pPinYin->pObj);
  //====================拼音选择状态按键==============================
  if(pPinYin->InState == LV_IME_PINYIN_PINYING_IN){
    //转到上一页拼音
    if(pPinYin->HavePrvNextBtn & 0x01){
      if(KeyId == 0){//上一页键上了
          unsigned char PrvContentBtnCount = pPinYin->BtnMax - 1;//除下一页按钮
          if(pPinYin->PinYinStart > PrvContentBtnCount)//还有上一页
            pPinYin->PinYinStart -= PrvContentBtnCount - 1;//除去上一页按钮
          else pPinYin->PinYinStart = 0;
          _UpdateDisp(pPinYin);
          return;
      }
      else KeyId--;//没在上一页键上
    }
    //转到下一页拼音
    if(pPinYin->HavePrvNextBtn & 0x02){
      if(KeyId == pPinYin->ContentBtnMax){//下一页键上了
        pPinYin->PinYinStart += pPinYin->ContentBtnMax;
        _UpdateDisp(pPinYin);
        return;
      }
    }
    //选择某个拼音了
    if(KeyId >= pPinYin->ContentBtnCount) return;//异常
    pPinYin->CurPinYin = pPinYin->PinYinStart + KeyId;
    _ToChSel(pPinYin); //进入汉字选择状态
    _UpdateDisp(pPinYin);
    return;
  }
  //===========================汉字选择状态按键============================
  unsigned char ChPos = KeyId; //汉字位置
  if(KeyId == 0){//在拼音键上按了一下：相当于按后撤键
    lvIME_PinYinKbKeyPro(pPinYin, LV_KEY_BACKSPACE);
    return;
  }
  KeyId--;
  //转到上一页汉字
  if(pPinYin->HavePrvNextBtn & 0x01){
    if(KeyId == 0){//上一页键上了
        unsigned char PrvContentBtnCount = pPinYin->BtnMax - 3;//除下一页与拼音按钮
        if(pPinYin->ChStart > PrvContentBtnCount)//还有上一页
          pPinYin->ChStart -= PrvContentBtnCount - 1;//除去上一页按钮
        else pPinYin->ChStart = 0;
        _UpdateDisp(pPinYin);
        return;
    }
    else KeyId--;//没在上一页键上
  }
  //转到下一页汉字
  if(pPinYin->HavePrvNextBtn & 0x02){
    if(KeyId == pPinYin->ContentBtnMax){//下一页键上了
      pPinYin->ChStart += pPinYin->ContentBtnMax;
      _UpdateDisp(pPinYin);
      return;
    }
  }
  //选择某个汉字了
  if(KeyId >= pPinYin->ContentBtnCount) return;//异常
  pPinYin->pEndCb(pPinYin->pvEndCb, lvIME_SignBuf[ChPos]);
  _ToWaitIn(pPinYin);//退至等待输入状态
  _UpdateDisp(pPinYin);
  return;
}

//-------------------------创建拼音输入法----------------------------------
//返回是否成功！
signed char lvIME_CreatePinYin(struct _lvIME_PinYin *pPinYin,  //主结构
                              lv_obj_t *pKeyG, //已创建的输入法软键盘
                              unsigned char IsKb26,   //是否为26键拼音
                              lvIME_PinYin_EndCb_t pEndCb, //输入完成回调
                              void *pvEndCb)          //回调参数
{
  //检查行是否能显示下
  unsigned char ContentBtnMax = (lv_obj_get_width(pKeyG) / LV_IME_PINYIN_CH_MIN_W);
  if(ContentBtnMax < 5) return -1; //太小了
  if(ContentBtnMax > LV_IME_PINYIN_BTN_MAX)
     ContentBtnMax = LV_IME_PINYIN_BTN_MAX;
  //初始化结构
  memset(pPinYin, 0, sizeof(struct _lvIME_PinYin));
  pPinYin->IsKb26 = IsKb26;
  pPinYin->pEndCb = pEndCb;
  pPinYin->pvEndCb = pvEndCb;
  pPinYin->BtnMax = ContentBtnMax;
  //创建横向铺满键盘,并附加在用户键盘上面
  lv_obj_t * pBtnM = lvIME_CreateKeyStart(_EventHandler, pPinYin);
  lv_obj_update_layout(pKeyG);
  int32_t h = lv_obj_get_height(pKeyG);
  h -= h % LV_IME_KB_ROW_HIGHT;//纯高对齐
  lv_obj_align(pBtnM, LV_ALIGN_BOTTOM_MID, 0, -2 - h);
  lv_obj_set_size(pBtnM, lv_pct(100), LV_IME_KB_ROW_HIGHT); //单行
  pPinYin->pObj = pBtnM;
  //更新键盘区
  _UpdateDisp(pPinYin);
}

//-------------------------软键盘按键处理函数---------------------------------
//返回0处理完成，1后撤到头部了，-1：不能识别的键值,-2拼音无效
signed char lvIME_PinYinKbKeyPro(struct _lvIME_PinYin *pPinYin,
                                unsigned char Key)
{
  //===========================26键拼音处理===========================
  if(pPinYin->IsKb26){
    unsigned char PinYinCount = pPinYin->PinYinCount;
    //后撤键
    if(Key == LV_KEY_BACKSPACE){
      if(!PinYinCount) return 1; //后撤到头部了
      pPinYin->PinYinCount--;
      pPinYin->PinYin[pPinYin->PinYinCount] = '\0';
      if(pPinYin->PinYinCount == 0) _ToWaitIn(pPinYin);//退至等待输入状态
      else pPinYin->pCh = PyIme_GetCh(pPinYin->PinYin);//更新汉字(一定有)
      _UpdateDisp(pPinYin);
      return 0;
    }
    if((Key < 'A') || (Key > 'Z')) return -1;//不能识别的键值
    pPinYin->PinYin[PinYinCount++] = Key + ('a' - 'A');//转小写拼音
    pPinYin->PinYin[PinYinCount] = '\0';
    const char *pCh = PyIme_GetCh(pPinYin->PinYin);//查表汉字
    if(pCh == NULL){//用户输错了，本次输入无效
      pPinYin->PinYin[PinYinCount - 1] = '\0';//后撤
      return -2; //拼音无效
    }
    //拼音输入有效了
    pPinYin->PinYinCount = PinYinCount;
    pPinYin->pCh = pCh;
    pPinYin->PinYinStart = 0;
    _ToChSel(pPinYin);
    _UpdateDisp(pPinYin);
    return 0;
  }
  //===========================9键拼音处理===========================
  //汉字选择状态
  if(pPinYin->InState == LV_IME_PINYIN_CH_SEL){
    if(Key == LV_KEY_BACKSPACE){//后撤键返回上级
      pPinYin->InState = LV_IME_PINYIN_PINYING_IN;
      _UpdateDisp(pPinYin);
      return 0;
    }
    //用户若按数字了，继续按拼音状态处理
  }
  //拼音输入状态处理
  if(Key == LV_KEY_BACKSPACE) Key = ' ';//后撤键转义
  else if((Key < '2') || (Key > '9')) return -1;//不能识别的键值
  unsigned char FindCount;
  //未找到时
  unsigned char PrvNumCount = pPinYin->PY.NumCount;
  if(NumKeyPyIme_pKey(&pPinYin->PY, &FindCount, Key) == NULL){
    if(Key == ' '){//后撤到头部了
      _ToWaitIn(pPinYin);//退至等待输入状态
      _UpdateDisp(pPinYin);
      if(PrvNumCount) return 0;//后撤有效,即刚后撤到头
      return 1; //后撤到头部了
    }
    return -2; //拼音无效
  }
  //找到了
  pPinYin->InState = LV_IME_PINYIN_PINYING_IN; //到拼音输入态
  _UpdateDisp(pPinYin);
  return 0;
}

//--------------------------------删除自身-------------------------------------
void lvIME_PinYinDelete(struct _lvIME_PinYin *pPinYin)
{
  if(pPinYin->pObj == NULL) return;
  lv_obj_delete(pPinYin->pObj);  //删除选择行
  pPinYin->pObj = NULL;
}
